/**
 * Y-Modem 协议实现
 * 张勇 41204@qq.com
 */

#pragma once

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif


/// 文件名最大长度
#define YMODEM_FILENAME_SIZE_MAX	120
/// 收发缓冲区大小
#define YMODEM_BUFFER_SIZE			(3 + 1024 + 2)

/// 用于发送数据的通信接口函数
typedef void (*ymodem_send_func_t)(void *cookie, const void *data, size_t size);

/// Y-Modem 状态
typedef enum _ymodem_state {
	YMODEM_STATE_IDLE = 0,					///< 空闲

	YMODEM_STATE_TX_WAIT_FOR_BEGIN,			///< 发送状态: 等待对方发送 CONT，以启动发送
	YMODEM_STATE_TX_WAIT_FOR_HEAD_ACK,		///< 发送状态: 文件头已发送，等待对方 ACK
	YMODEM_STATE_TX_WAIT_FOR_HEAD_CONT,		///< 发送状态: 文件头已发送，等待对方 CONT
	YMODEM_STATE_TX_WAIT_FOR_DATA_ACK,		///< 发送状态: 文件数据已发送，等待对方 ACK
	YMODEM_STATE_TX_WAIT_FOR_EOT1_NAK,		///< 发送状态: EOT1 已发送，等待对方 ACK/NAK
	YMODEM_STATE_TX_WAIT_FOR_EOT2_ACK,		///< 发送状态: EOT2 已发送，等待对方 ACK
	YMODEM_STATE_TX_WAIT_FOR_EOT2_CONT,		///< 发送状态: EOT2 已发送，等待对方 CONT
	YMODEM_STATE_TX_WAIT_FOR_FIN_ACK,		///< 发送状态: 文件传输结束帧已发送，等待对方 ACK

	YMODEM_STATE_RX_WAIT_FOR_HEAD,			///< 接收状态: 等待对端发送文件头信息
	YMODEM_STATE_RX_WAIT_FOR_DATA,			///< 接收状态: 等待对端发送文件数据
	YMODEM_STATE_RX_WAIT_FOR_EOT1,			///< 接收状态: 等待对端发送 EOT1
	YMODEM_STATE_RX_WAIT_FOR_EOT2,			///< 接收状态: 等待对端发送 EOT2
	YMODEM_STATE_RX_WAIT_FOR_FIN,			///< 接收状态: 等待对端发送 FIN

	YMODEM_STATE_MAX
} ymodem_state_t;

/// Y-Modem 数据结构
typedef struct _ymodem {
	/// 数据缓冲区(大小应大于 YMODEM_BUFFER_SIZE)
	uint8_t* buf;
	/// 用于发送数据的接口
	ymodem_send_func_t sendFunc; void* sendCookie;
	
	/// 用于发送文件任务：需要读取文件时，被回调
	/// @param data [out] 数据缓冲区
	/// @param size [in] 数据缓冲区大小; [out] 实际读取的数据长度
	/// @return 错误。STD_ERR_EOF 表示文件结束
	std_err_t (*reader)(struct _ymodem* modem, uint8_t* data, uint32_t *size);
	
	/// 用于接收文件任务：处理接收到的文件头函数
	/// @return 返回任何错误码都会停止文件传输
	std_err_t (*header)(struct _ymodem* modem, const char* fname, uint32_t fsize);
	
	/// 用于接收文件任务：处理接收到的文件数据块函数
	/// @param data 文件数据，当遇到文件结束时，为 NULL
	/// @param size 文件数据长度，当遇到文件结束时，为 0
	std_err_t (*writer)(struct _ymodem* modem, uint8_t* data, uint32_t size);
	
	/// 文件传送结束回调函数原型
	/// @param code 错误码
	void (*completer)(struct _ymodem* modem, int code);
	
    // 当前缓冲区中数据长度
    uint16_t bpos;
	// 帧序号
	uint8_t fsn;
	// session 状态
	ymodem_state_t state;
	// session 错误码
	std_err_t err;
	// 持续定时发送 CONT 的任务 ID
	unios_task_t contRepeaterTaskId;
	// 超时重发任务 ID
	unios_task_t timeoutTaskId;
	// 当前重发次数
	uint16_t retryCount;
	// 当前文件总大小
	uint32_t fsize;
	// 当前文件已接收/发送的数据长度
	uint32_t fpos;
	/// 本地文件句柄
	unios_file_t flocal;
	/// 最大超时等待时间(ms)
	uint32_t TIMEOUT_MAX;
	/// 最大重发次数
	uint32_t RETRY_COUNT_MAX;
} ymodem_t;

/// 开始接收文件
std_err_t ymodem_start_receive(ymodem_t* modem);
/// 开始传输文件
std_err_t ymodem_start_transmit(ymodem_t* modem, const char* fname, uint32_t fsize);
/// 复位当前状态(如果正在发送或接收，则先停止)
std_err_t ymodem_reset(ymodem_t* modem, std_err_t errcode);
/// 当接收到一个字节
std_err_t ymodem_on_rx_byte(ymodem_t* modem, uint8_t ch);
/// 传输是否结束
bool ymodem_is_transfer_completed(ymodem_t* modem);


#ifdef __cplusplus
}
#endif
